package org.cedar.syntaxcore;

/**
 * <p>Title: 运算单元</p>
 * <p>Description:对前缀式队列进行处理 </p>
 * <p>Copyright: Copyright (c) 2005</p>
 * <p>Company: </p>
 * @author net.wzh
 * @version 1.0
 */

import java.text.ParseException;
import java.util.Date;
import java.util.List;
import java.util.Stack;

import org.cedar.exceptions.CalcException;


public class Calculator {
  private Stack calcStack;
  private SyntaxContext ctx = new SyntaxContext();
  public Calculator() {
    calcStack = new Stack();
  }

  public Calculator(SyntaxContext variantContext) {
      this();
      if(null != variantContext)
    	  ctx = variantContext;
  }

  public MetaElement calcIt(Queue eq) throws CalcException {
    ExecutionItem ei;
    eq.resetPos();
    if (eq.isEmpty())
      return new MetaElement("");
    while (!eq.isEmpty()) {
      ei = (ExecutionItem) eq.deQueue();
      switch (ei.itemType) {
        case ItemType.itBool:
          if ("true".equalsIgnoreCase(ei.itemString))
            calcStack.push(new MetaElement(new Boolean(true)));
          else
            calcStack.push(new MetaElement(new Boolean(false)));
          break;
        case ItemType.itString:
          calcStack.push(new MetaElement(ei.itemString));
          break;
        case ItemType.itDigit:
		  Number n = null;
		  try {
			n = convert(ei.itemString);
			calcStack.push(new MetaElement(n));
		  }catch (Exception e1) {
			throw new CalcException(1000);
		  }
          break;
        case ItemType.itDate:
           Date dt = null;
           try {
             dt = DateUtil.parseDate(ei.itemString);
           }
           catch(ParseException e){
               throw new CalcException(1001);
           }
           calcStack.push(new MetaElement(dt));
           break;
        case ItemType.itOperator:
          doOperator(ei.itemOperator);
          break;
        case ItemType.itFunction:
          excuteFunction(ei);
          break;
        case ItemType.itVariable:
          calcStack.push(new MetaElement(ei.itemString,ei.itemParams,ctx));
          break;
        default:
          throw (new CalcException(1002));

      }
    }
    if(calcStack.size() > 0)
        return (MetaElement) calcStack.pop();
    return null;
  }

  private Number convert(String numStr)throws Exception{
	  Number ret = null;
	  String numS = numStr.toUpperCase();
	  try{
		  if(numS.endsWith("L")){
			  ret = Long.parseLong(numS.substring(0,numS.length()-1));
		  }else if(numS.endsWith("D")){
			  ret = Double.parseDouble(numS.substring(0,numS.length()-1));
		  }else if(numS.indexOf(".") > -1){
			  ret = Double.parseDouble(numS);
		  }else{
	          ret = Integer.parseInt(numS);
		  }
       }catch (Exception e) {
          try {
            ret = Integer.parseInt(numS);
          }catch (Exception e1) {
        	  try{
        		  ret = Double.parseDouble(numS);
        	  } catch (Exception e2) {
        		  try{
        			  ret = Long.parseLong(numS);
        		  }catch (Exception e3) {
        			  throw new CalcException(1000);
        		  }
        	  }
          }
        }
       return ret;
  }
  private void excuteFunction(ExecutionItem ei) throws CalcException {
    MetaElement v;
    FunctionDesc fd = new FunctionDesc(ei.itemString);
    for (int i = 0; i < ei.itemParams.size(); i++) {
      String s = (String) ei.itemParams.get(i);
      Parser p = new Parser();
      p.parseIt(s);
      Queue eq;
      eq = p.eqResult;
      Calculator c = new Calculator(this.ctx);
      try {
        fd.add(c.calcIt(eq));
      }
      catch (Exception ex) {
    	  throw new CalcException(4024, ex.getMessage());
      }
    }
    v = ctx.embeddedFunction(fd);
    if (v.valueType != VariantType.vtUnknow)
      calcStack.push(v);
    else
      throw new CalcException(1003, ei.itemString);
  }

  private void doOperator(int op) throws CalcException {
    switch (op) {
      case EnmOperators.UnMinus:
      case EnmOperators.Nop:
      case EnmOperators.Not:
      case EnmOperators.UnPlus:
      case EnmOperators.Assign:
        if (calcStack.size() < 1)
          throw new CalcException(1004, op);
        break;
      default:
        if (calcStack.size() < 2)
          throw (new CalcException(1004, op));
        break;
    }

    switch (op) {
      case EnmOperators.UnMinus:
      case EnmOperators.Not:
        calcStack.push( (ctx.not_( (MetaElement) calcStack.pop())));
        break;
      case EnmOperators.UnPlus:
        break;
      case EnmOperators.Plus:
        calcStack.push(ctx.plus_( (MetaElement) calcStack.pop(),
                                         (MetaElement) calcStack.pop()));
        break;
      case EnmOperators.Minus:
        calcStack.push(ctx.minus_( (MetaElement) calcStack.pop(),
                                          (MetaElement) calcStack.pop()));
        break;
      case EnmOperators.Mul:
        calcStack.push(ctx.multi_( (MetaElement) calcStack.pop(),
                                          (MetaElement) calcStack.pop()));
        break;
      case EnmOperators.Div:
        calcStack.push(ctx.div_( (MetaElement) calcStack.pop(),
                                        (MetaElement) calcStack.pop()));
        break;
      case EnmOperators.Mod:
    	  calcStack.push(ctx.mod_( (MetaElement) calcStack.pop(),
    			  (MetaElement) calcStack.pop()));
    	  break;
      case EnmOperators.Gr:
        calcStack.push(ctx.lg( (MetaElement) calcStack.pop(),
                                      (MetaElement) calcStack.pop()));
        break;
      case EnmOperators.Ls:
        calcStack.push(ctx.lt( (MetaElement) calcStack.pop(),
                                      (MetaElement) calcStack.pop()));
        break;
      case EnmOperators.GrEq:
        calcStack.push(ctx.lge( (MetaElement) calcStack.pop()
                                       , (MetaElement) calcStack.pop()));
        break;
      case EnmOperators.LsEq:
        calcStack.push(ctx.lte( (MetaElement) calcStack.pop()
                                       , (MetaElement) calcStack.pop()));
        break;
      case EnmOperators.Eq:
        calcStack.push(ctx.eq( (MetaElement) calcStack.pop(),
                                      (MetaElement) calcStack.pop()));
        break;
      case EnmOperators.NtEq:
        calcStack.push(ctx.notEq_( (MetaElement) calcStack.pop(),
                                          (MetaElement) calcStack.pop()));
        break;
      case EnmOperators.And:
        calcStack.push(ctx.and_( (MetaElement) calcStack.pop(),
                                        (MetaElement) calcStack.pop()));
        break;
      case EnmOperators.Or:
        calcStack.push(ctx.or_( (MetaElement) calcStack.pop(),
                                       (MetaElement) calcStack.pop()));
        break;
      case EnmOperators.Assign:
          MetaElement right = (MetaElement) calcStack.pop();
          MetaElement left = (MetaElement) calcStack.pop();
          this.ctx.put((String)left.value,(List)left.params,right.value());
          break;
      default:
        throw new CalcException(1005, String.valueOf(op));
    }
  }


}
